// Google test includes
#include <gtest/gtest.h>

// Standard include
#include <memory>

// Service includes
#include "Printers/Printer.h"
#include "Printers/ExplicitPrinter.h"
#include "Printers/PrettyPrinter.h"
#include "Printers/PostfixPrinter.h"
#include "Parsers/PostfixParser.h"

// Node types
#include "Nodes/Binary/Node.h"
#include "Obsolete/MultipleNode.h"

TEST(Integration, ParseThenPostfixPrintBinaryNode)
{
    NodeFactory<BinaryNode> factory;
    std::auto_ptr<BinaryNode> pNode(postfixParse<BinaryNode>("AB|C&~D|~~EF|&ABC&|~&~", factory));

	EXPECT_EQ("AB|C&~D|~~EF|&ABC&|~&~", Printer<PostfixPrinter>::print(pNode.get()));
}

TEST(Integration, ParseThenPrettyPrintBinaryNode)
{
    NodeFactory<BinaryNode> factory;
    std::auto_ptr<BinaryNode> pNode(postfixParse<BinaryNode>("AB|C&~D|~~EF|&ABC&|~&~", factory));

	EXPECT_EQ("~(~~(~((A | B) & C) | D) & (E | F) & ~(A | B & C))", Printer<PrettyPrinter>::print(pNode.get())); 
}

TEST(Integration, ParseThenExplicitPrintBinaryNode)
{
    NodeFactory<BinaryNode> factory;
    std::auto_ptr<BinaryNode> pNode(postfixParse<BinaryNode>("AB|C&~D|~~EF|&ABC&|~&~", factory));

	EXPECT_EQ("~((~~(~((A | B) & C) | D) & (E | F)) & ~(A | (B & C)))", Printer<ExplicitPrinter>::print(pNode.get()));
}


TEST(Integration, ParseThenPostfixPrintMultipleNode)
{
    NodeFactory<MultipleNode> factory;
    std::auto_ptr<MultipleNode> pNode(postfixParse<MultipleNode>("AB|C&~D|~~EF|&ABC&|~&~", factory));

	EXPECT_EQ("AB|C&~D|~~EF|&ABC&|~&~", Printer<PostfixPrinter>::print(pNode.get())); 
}

TEST(Integration, ParseThenPrettyPrintMultipleNode)
{
    NodeFactory<MultipleNode> factory;
    std::auto_ptr<MultipleNode> pNode(postfixParse<MultipleNode>("AB|C&~D|~~EF|&ABC&|~&~", factory));

	EXPECT_EQ("~(~~(~((A | B) & C) | D) & (E | F) & ~(A | B & C))", Printer<PrettyPrinter>::print(pNode.get())); 
}

TEST(Integration, ParseThenExplicitPrintMultipleNode)
{
    NodeFactory<MultipleNode> factory;
    std::auto_ptr<MultipleNode> pNode(postfixParse<MultipleNode>("AB|C&~D|~~EF|&ABC&|~&~", factory));

	EXPECT_EQ("~(~~(~((A | B) & C) | D) & (E | F) & ~(A | (B & C)))", Printer<ExplicitPrinter>::print(pNode.get())); 
}

TEST(Integration, ParseThenPrint1)
{   
	NodeFactory<BinaryNode> factory;
	std::auto_ptr<BinaryNode> pNode(postfixParse<BinaryNode>("AB|C|ABC||&~AB&C&ABC&&|~&", factory));
    EXPECT_EQ("(~(((A | B) | C) & (A | (B | C))) & ~(((A & B) & C) | (A & (B & C))))", Printer<ExplicitPrinter>::print(pNode.get()));
}

TEST(Integration, ParseThenPrint2)
{ 
	NodeFactory<MultipleNode> factory;
	std::auto_ptr<MultipleNode> pNode(postfixParse<MultipleNode>("AB|C|", factory));
    EXPECT_EQ("(A | B | C)", Printer<ExplicitPrinter>::print(pNode.get()));

}

TEST(Integration, ParseThenPrint3)
{
	NodeFactory<MultipleNode> factory;
	std::auto_ptr<MultipleNode> pNode(postfixParse<MultipleNode>("ABC||", factory));
    EXPECT_EQ("(A | B | C)", Printer<ExplicitPrinter>::print(pNode.get()));

}

TEST(Integration, ParseThenPrint4)
{   
	NodeFactory<MultipleNode> factory;
	std::auto_ptr<MultipleNode> pNode(postfixParse<MultipleNode>("ABC&&", factory));
    EXPECT_EQ("(A & B & C)", Printer<ExplicitPrinter>::print(pNode.get()));

}

TEST(Integration, ParseThenPrint5)
{   
	NodeFactory<MultipleNode> factory;
	std::auto_ptr<MultipleNode> pNode(postfixParse<MultipleNode>("AB|C|ABC||&~AB&C&ABC&&|~&", factory));
    EXPECT_EQ("(~((A | B | C) & (A | B | C)) & ~((A & B & C) | (A & B & C)))", Printer<ExplicitPrinter>::print(pNode.get()));

}


/*
TEST(Integration, ParseThenPrint)
{   
	std::auto_ptr<Node> pNode(parse<Node>(, factory));
    EXPECT_EQ("", Printer<ExplicitPrinter>::print(pNode.get()));

}
*/
#if 0

#include "NullFactory.h"
#include "NNFProcessor.h"
//#include "Traverser.h"

#include "Benchmark.h"
#include "BenchmarkParse.h"

const size_t giIterationsCount = 100000;

template<class Node>
void TestConvertNNF(const std::string & sInput)
{
    std::cout << std::endl << "Converting to NNF..." << std::endl;
    NodeFactory<Node> factory;
    std::auto_ptr<Node> pNode(postfixParse<Node>(sInput, factory));
    std::cout << "Parsed result: " << Printer<>::print(pNode.get()) << std::endl;
    bool bRes = inplaceProcessNNF(pNode, factory);
    std::cout << "Processed result (" << bRes << "): " << Printer<ExplicitPrinter>::print(pNode.get()) << std::endl;
}

int main(int argc, char ** argv)
{
    TestParse2<MultipleNode>("AB|C|", VisitorNodeFactory());
    TestParse2<MultipleNode>("ABC||", VisitorNodeFactory());
    TestParse2<MultipleNode>("AB&C&", VisitorNodeFactory());
    TestParse2<MultipleNode>("ABC&&", VisitorNodeFactory());
    TestParse2<MultipleNode>("AB|C|ABC||&~AB&C&ABC&&|~&", VisitorNodeFactory());

    TestConvertNNF<BinaryNode>("AB&C&~");
    TestConvertNNF<MultipleNode>("AB&C&~");

/*
    bool bRes;
    // testing with ApplyOnce policy
    std::auto_ptr<INode> pNewNode(pNode->clone());
    std::cout << "Normalizing: " << pNewNode->print() << std::endl;
    bRes = Traverse<ApplyOnce>(pNewNode, NNFProcessor(bf));
    std::cout << "Processing result (" << bRes << "): " << pNewNode->print() << std::endl;
    bRes = Traverse<ApplyOnce>(pNewNode, NNFProcessor(bf));
    std::cout << "Processing result (" << bRes << "): " << pNewNode->print() << std::endl;
    bRes = Traverse<ApplyOnce>(pNewNode, NNFProcessor(bf));
    std::cout << "Processing result (" << bRes << "): " << pNewNode->print() << std::endl;
    bRes = Traverse<ApplyOnce>(pNewNode, NNFProcessor(bf));
    std::cout << "Processing result (" << bRes << "): " << pNewNode->print() << std::endl;

    // testing with ApplyUntilUnchanged policy
    pNewNode.reset(pNode->clone());
    std::cout << "Normalizing: " << pNewNode->print() << std::endl;
    bRes = Traverse<ApplyUntilUnchanged>(pNewNode, NNFProcessor(bf));
    std::cout << "Processing result (" << bRes << "): " << pNewNode->print() << std::endl;
    bRes = Traverse<ApplyUntilUnchanged>(pNewNode, NNFProcessor(bf));
    std::cout << "Processing result (" << bRes << "): " << pNewNode->print() << std::endl;
    bRes = Traverse<ApplyUntilUnchanged>(pNewNode, NNFProcessor(bf));
    std::cout << "Processing result (" << bRes << "): " << pNewNode->print() << std::endl;
    bRes = Traverse<ApplyUntilUnchanged>(pNewNode, NNFProcessor(bf));
    std::cout << "Processing result (" << bRes << "): " << pNewNode->print() << std::endl;
*/
/*
    std::auto_ptr<INode> pNewNode(Traverse(pNode.get(), NNFProcessor(bf)));
    std::cout << "Processed result: " << pNewNode->print() << std::endl;
    std::auto_ptr<INode> pNewNode2(Traverse(pNewNode.get(), NNFProcessor(bf)));
    std::cout << "Processed result: " << pNewNode2->print() << std::endl;
    std::auto_ptr<INode> pNewNode3(Traverse(pNewNode2.get(), NNFProcessor(bf)));
    std::cout << "Processed result: " << pNewNode3->print() << std::endl;
    std::auto_ptr<INode> pNewNode4(Traverse(pNewNode3.get(), NNFProcessor(bf)));
    std::cout << "Processed result: " << pNewNode4->print() << std::endl;
*/


    std::cout << std::endl << std::endl << "Running benchmarks" << std::endl;
//    std::string sInputString("AB|C&~D|~~EF|&");
    std::string sInputString("AB|C|ABC||&~AB&C&ABC&&|~&");
    benchmark(ParseBenchmark<BinaryNode>(sInputString), giIterationsCount);
    benchmark(ParseBenchmark<MultipleNode>(sInputString), giIterationsCount);
    benchmark(ParseBenchmark<MultipleNode, VisitorNodeFactory>(sInputString), giIterationsCount);
    benchmark(ParseBenchmark<NullNode>(sInputString), giIterationsCount);

    return 0;
}
#endif

